<?php
namespace bdhert\PhpStruct;

/**
 * 标准结构体
 * Class Standard
 * @package bdhert\PhpStruct
 */
abstract class Standard implements \ArrayAccess, \Iterator {
    // 控制信息
    protected int               $_index      = 0;
    protected string            $single_name = 'value';
    protected ?\ReflectionClass $reflection  = NULL;

    /**
     * Standard constructor.
     * @param array $params
     * @throws \ReflectionException
     */
    public function __construct(array $params = []) {
        $this->load($params);
    }

    /**
     * 重载
     * @param array $params
     * @return $this
     * @throws \ReflectionException
     */
    public function reload(array $params = []): self {
        $this->load($params);

        return $this;
    }

    /**
     * 加载参数
     * @param array $params
     * @throws \ReflectionException
     */
    protected function load(array $params): void {
        $r = $this->getRef();
        $p = $this->getParams();

        // 结构嵌套定义
        foreach ($params as $param_name => $param_value) {
            if (!$r->hasProperty($param_name)) continue;

            unset($p[$param_name]);

            $prop_type = $r->getProperty($param_name)->getType();
            $prop_name = $prop_type ? $prop_type->getName() : NULL;

            if ($prop_name && is_subclass_of($prop_name, self::class)
            && !($param_value instanceof self)) {
                $init_values = is_array($param_value) ? $param_value : [$this->single_name => $param_value];

                $this->{$param_name} = call_user_func("{$prop_name}::build", $init_values);
                continue;
            }

            $this->{$param_name} = $param_value;
        }

        // 未定义枚举值重载
        foreach ($p as $p_k => $p_v) {
            $prop_type = $r->getProperty($p_k)->getType();
            $prop_name = $prop_type ? $prop_type->getName() : NULL;

            if ($prop_name && is_subclass_of($prop_name, Enum::class)) {
                $this->{$p_k} = call_user_func("{$prop_name}::build");
            }
        }

        if (method_exists($this, 'init')) $this->init();
    }

    protected function getParams(): array {
        $r = $this->getRef();
        $p = array_column($r->getProperties(), NULL, 'name');

        $nn = ['reflection', '_index', 'single_name'];
        foreach ($nn as $n) {
            unset($p[$n]);
        }

        return $p;
    }

    /**
     * 获取反射信息
     * @return \ReflectionClass
     */
    protected function getRef(): \ReflectionClass {
        if ($this->reflection) return $this->reflection;

        return ($this->reflection = new \ReflectionClass(static::class));
    }

    /**
     * 多值创建
     * @param array $params
     * @return static
     * @throws \ReflectionException
     */
    public static function build(array $params = []): self {
        return new static($params);
    }

    /**
     * 单值创建
     * @param $value
     * @return static
     * @throws \ReflectionException
     */
    public static function var($value): self {
        $i = new static();

        return $i->reload([$i->getSingleName() => $value]);
    }

    public function getSingleName(): string {
        return $this->single_name;
    }

    public function offsetExists($offset) {
        return isset($this->$offset);
    }

    public function offsetGet($offset) {
        return $this->$offset;
    }

    public function offsetSet($offset, $value) {
        $this->$offset = $value;
    }

    public function offsetUnset($offset) {
        unset($this->$offset);
    }

    public function current() {
        return $this->getMember()[$this->key()] ?? NULL;
    }

    public function next() {
        $this->_index++;
    }

    public function valid() {
        return $this->_index > -1 && $this->_index < count($this->getMember());
    }

    public function key() {
        return array_keys($this->getMember())[$this->_index] ?? NULL;
    }

    public function rewind() {
        $this->_index = 0;
    }

    public function toArray() {
        return $this->getMember();
    }

    public function toJson() {
        return json_encode($this->toArray(), JSON_UNESCAPED_UNICODE);
    }

    public function __get($property_name) {
        return isset($this->{$property_name}) ? $this->{$property_name} : NULL;
    }

    public function __set($property_name, $value) {
        if (isset($this->{$property_name})) $this->$property_name = $value;
    }

    /**
     * 获取所有成员
     * @return array
     */
    private function getMember(): array {
        $vars   = get_object_vars($this);
        $member = [];

        foreach ($vars as $key => $value) {
            if (is_object($value) && ($value instanceof self)) {
                $value = ($value instanceof Enum) ? $value->value : $value->toArray();
            }

            $member[$key] = $value;
        }

        foreach (['_index', 'single_name', 'reflection'] as $hide) {
            unset($member[$hide]);
        }

        return $member;
    }
}